using System;
using System.Collections.Generic;
using System.Linq;
using ParserGenerator.Extensions;

namespace ParserGenerator.CharacterMapping
{
    internal static class CharIntervalHelpers
    {
        public static void Normalize(List<CharInterval> items)
        {
            if (items == null)
                throw new ArgumentNullException(nameof(items));
            items.Sort();
            int v = 0;
            int i = 0;
            while (i < items.Count)
            {
                CharInterval item = items[i];
                int first = item.First;
                int last = item.Last;
                while (++i < items.Count && items[i].First - 1 <= last)
                    last = Math.Max(last, items[i].Last);
                items[v++] = new CharInterval(first, last);
            }
            items.RemoveRange(v, items.Count - v);
        }

        public static void Negate(List<CharInterval> items, CharInterval universal)
        {
            if (items == null)
                throw new ArgumentNullException(nameof(items));
            int t = universal.First - 1;
            int v = 0;
            for (int i = 0; i < items.Count; i++)
            {
                CharInterval item = items[i];
                int first = item.First;
                int last = item.Last;
                if (t + 1 < first)
                    items[v++] = new CharInterval(t + 1, first - 1);
                t = last;
            }
            items.RemoveRange(v, items.Count - v);
            if (t + 1 <= universal.Last)
                items.Add(new CharInterval(t + 1, universal.Last));
        }

        public static void Merge(List<CharInterval> value, IEnumerable<CharInterval> other)
        {
            if (value == null)
                throw new ArgumentNullException(nameof(value));
            if (other == null)
                throw new ArgumentNullException(nameof(other));
            value.AddRange(other);
            Normalize(value);
        }

        public static List<CharInterval> Difference(CharClass value, CharClass other)
        {
            if (value == null)
                throw new ArgumentNullException(nameof(value));
            if (other == null)
                throw new ArgumentNullException(nameof(other));
            List<CharInterval> items = new List<CharInterval>(value.Intervals);
            Negate(items, value.Universal);
            Merge(items, other.Intervals);
            Negate(items, value.Universal);
            return items;
        }

        public static string FormatChar(int c)
        {
            if (c < CharExtensions.MinChar || c > CharExtensions.MaxChar)
                throw new ArgumentOutOfRangeException(nameof(c), SR.InvalidUnicodeCodepoint);
            return c.AsSingleQuote();
        }

        public static string FormatInterval(CharInterval item, Func<int, string> format)
        {
            if (format == null)
                throw new ArgumentNullException(nameof(format));
            if (item.First == item.Last)
                return format(item.First);
            else if (item.First + 1 == item.Last)
                return format(item.First) + " " + format(item.Last);
            else if (item.First + 2 == item.Last)
                return format(item.First) + " " + format(item.First + 1) + " " + format(item.Last);
            else
                return format(item.First) + "-" + format(item.Last);
        }

        public static string FormatInterval(CharInterval item)
        {
            return FormatInterval(item, FormatChar);
        }

        public static string FormatIntervals(IEnumerable<CharInterval> items, bool negated, Func<int, string> format)
        {
            if (items == null)
                throw new ArgumentNullException(nameof(items));
            string str = string.Join(" ", from item in items select FormatInterval(item, format));
            if (negated)
                return "[^" + str + "]";
            else
                return "[" + str + "]";
        }

        public static string FormatIntervals(IEnumerable<CharInterval> items, bool negated)
        {
            if (items == null)
                throw new ArgumentNullException(nameof(items));
            return FormatIntervals(items, negated, FormatChar);
        }

        public static string FormatIntervals(IEnumerable<CharInterval> items)
        {
            if (items == null)
                throw new ArgumentNullException(nameof(items));
            return FormatIntervals(items, false);
        }

        public static string FormatClass(IEnumerable<CharInterval> items, CharInterval universal, Func<int, string> format)
        {
            if (items == null)
                throw new ArgumentNullException(nameof(items));
            List<CharInterval> negative = new List<CharInterval>(items);
            Negate(negative, universal);
            string n = FormatIntervals(negative, true, format);
            string p = FormatIntervals(items, false, format);
            string str = (n.Length < p.Length) ? n : p;
            return str;
        }

        public static string FormatClass(IEnumerable<CharInterval> items, CharInterval universal)
        {
            if (items == null)
                throw new ArgumentNullException(nameof(items));
            return FormatClass(items, universal, FormatChar);
        }

        public static string FormatClass(IEnumerable<CharInterval> items)
        {
            if (items == null)
                throw new ArgumentNullException(nameof(items));
            return FormatClass(items, CharInterval.Universal);
        }
    }
}
